﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using HttpWebAdapters;
using TC.Vacation.Spider.Core;
using TC.Vacation.Spider.Events;
using TC.Vacation.Spider.Loggers;
using TC.Vacation.Spider.Rules;
using TC.Vacation.Spider.Web;

namespace TC.Vacation.Spider
{
    public class Spider : IDisposable
    {
        /// <summary>
        /// 抓取配置
        /// </summary>
        protected static SpiderOption option;
        /// <summary>
        /// 线程安全的无序队列
        /// </summary>
        Lazy<ConcurrentBag<string>> urlQueue = new Lazy<ConcurrentBag<string>>();
        /// <summary>
        /// 异步请求的配置信息
        /// </summary>
        private ServerConfig config;

        /// <summary>
        /// 构造函数初始化
        /// </summary>
        /// <param name="spider">爬取配置</param>
        public Spider(SpiderOption spider)
        {
            option = spider;
            option.Analyzer = this.Analyzer;

            this.Merge();

            InitSpider();
        }

        /// <summary>
        /// Merges this instance.
        /// </summary>
        private void Merge()
        {
            config = new ServerConfig
            {
                UserAgent = option.UserAgent,
                Timeout = TimeSpan.FromSeconds(option.Timeout),
                Encoding = Encoding.UTF8,
                KeepAlive = option.KeepAlive,
                ContentType = option.ContentType,
                Mehtod = !string.IsNullOrEmpty(option.Method) ? (option.Method.ToLower() == "post" ? HttpWebRequestMethod.POST : HttpWebRequestMethod.GET) : HttpWebRequestMethod.POST,
                Address = option.ProxyHost,
                Port = option.ProxyPort,
                Async = option.Async,
                UseProxy = option.UseProxy
            };
        }

        /// <summary>
        /// Gets the bag.
        /// </summary>
        /// <value>
        /// The bag.
        /// </value>
        internal ConcurrentBag<string> Bag
        {
            get
            {
                return urlQueue.Value;
            }
        }

        /// <summary>
        /// 初始化爬取
        /// </summary>
        private void InitSpider()
        {
            if (option.InitSeeds != null)
            {
                option.InitSeeds.ForEach(x => { Bag.Add(x); });
            }
        }

        /// <summary>
        /// 动态加载插件
        /// </summary>
        /// <returns></returns>
        public RulesAnalyzer Analyzer
        {
            get
            {
                if (option.Analyzer == null)
                {
                    if (!string.IsNullOrEmpty(option.PluginName))
                        return DynamicLoadPlugin.Instent.Get(option.PluginName);
                    else
                        return null;
                }
                else
                {
                    return option.Analyzer;
                }
            }
        }

        /// <summary>
        /// Adds the request.
        /// </summary>
        /// <param name="url">The URL.</param>
        /// <param name="success">The success.</param>
        /// <param name="error">The error.</param>
        public Spider AddRequest(string url, Action<DataReceivedEventArgs> success, Action<SpiderErrorEventArgs> error)
        {
            Bag.Add(url);
            this.Start(success, error);
            return this;
        }

        /// <summary>
        /// Adds the request.
        /// </summary>
        /// <param name="url">The URL.</param>
        public Spider AddRequest(string url)
        {
            Bag.Add(url);
            return this;
        }

        /// <summary>
        /// Adds the request.
        /// </summary>
        /// <param name="url">The URL.</param>
        public Spider AddRequest(List<string> url)
        {
            url.ForEach(x =>
            {
                Bag.Add(x);
            });
            return this;
        }

        /// <summary>
        /// 开始采集
        /// </summary>
        /// <param name="success">成功抓取数据后的回调函数</param>
        /// <param name="error">抓取出错时的回调函数</param>
        public void Start(Action<DataReceivedEventArgs> success, Action<SpiderErrorEventArgs> error)
        {
            int tick = 0;
            string url = string.Empty;
            while (Bag.TryTake(out url))
            {
                Interlocked.Increment(ref tick);
                try
                {
                    AsyncWebRequestManager.StartAsyncWebRequest(url,
                    x =>
                    {
                        var received = new DataReceivedEventArgs
                        {
                            Html = x.Html,
                            Url = x.Url,
                            HttpStatus = x.HttpStatus,
                            Spent = x.Spent,
                            HttpStatusDescription = x.HttpStatusDescription,
                            Tick = tick
                        };
                        if (option.Analyzer != null)
                        {
                            received.DynamicEntity = option.Analyzer.Filter(received.Html,url);

                            received.Html = received.Html ?? "";
                        }
                        //logger
                        Logger.Write(string.Format("数据抓取成功：{0}，本次耗时{1}ms。", url, x.Spent));

                        success(received);
                    },
                    p =>
                    {
                        var args = new SpiderErrorEventArgs
                        {
                            Error = p.Error,
                            Exception = p.WebException,
                            Url = p.Url,
                            Spent = p.Spent,
                            ErrorHtml = p.Html
                        };
                        error(args);
                    }, config);
                }
                catch (Exception exception)
                {
                    var args = new SpiderErrorEventArgs
                    {
                        Error = exception.Message,
                        Exception = exception,
                        Url = url
                    };
                    error(args);
                }
            }
        }

        /// <summary>
        /// 释放资源
        /// </summary>
        public void Dispose()
        {
            AsyncWebRequestManager.CancelAsyncEvents();
        }
    }
}